package com.ywu.boot.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.commands.JedisCommands;
import redis.clients.jedis.params.SetParams;

import javax.annotation.PostConstruct;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName RedisUtils
 * @Description TODO
 * @Author GroundDemo
 * @Date 2024/3/16 17:00
 * @Version 1.0
 **/
@Component
@Slf4j
@ConditionalOnClass(StringRedisTemplate.class)
public class RedisUtils {
    @Autowired
    private StringRedisTemplate redisTemplate;

    private static StringRedisTemplate staticRedisTemplate;

    private static final StringBuilder DELETE_LOCK_LUA = new StringBuilder();

    static {
        // 先查询当前Key的Uid值是否与传入的值一致，如果一致则删除，否则删除失败
        DELETE_LOCK_LUA.append("if redis.call(\"get\", KEYS[1]) == ARGV[1] ");
        DELETE_LOCK_LUA.append("then ");
        DELETE_LOCK_LUA.append("   return redis.call(\"del\", KEYS[1]) ");
        DELETE_LOCK_LUA.append("else ");
        DELETE_LOCK_LUA.append("   return 0 ");
        DELETE_LOCK_LUA.append("end ");
    }

    @PostConstruct
    public void init() {
        staticRedisTemplate = redisTemplate;
    }

    /**
    * @Author GroundDemo
    * @Description 设置redis的值
    * @Date 17:02 2024/3/16
    * @Param [key, value]
    * @return void
    **/
    public static void set(String key, String value) {
        staticRedisTemplate.opsForValue().set(key, value);
    }

    /**
    * @Author GroundDemo
    * @Description 添加hash键
    * @Date 10:33 2025/3/22
    * @Param [key, hashKey, hashValue]
    * @return void
    **/
    public static void hashSet(String key, String hashKey, String hashValue) {
        staticRedisTemplate.opsForHash().put(key, hashKey, hashValue);
    }

    /**
    * @Author GroundDemo
    * @Description 设置键的过期时间
    * @Date 17:41 2024/3/16
    * @Param [key, time, unit]
    * @return void
    **/
    public static void setExpire(String key, long time, TimeUnit unit) {
        staticRedisTemplate.expire(key, time, unit);
    }

    /**
    * @Author GroundDemo
    * @Description 根据KEY获取值
    * @Date 17:54 2024/3/16
    * @Param [key]
    * @return java.lang.String
    **/
    public static String getByKey(String key) {
        if (Boolean.FALSE.equals(staticRedisTemplate.hasKey(key))) {
            return null;
        }
        return staticRedisTemplate.opsForValue().get(key);
    }

    /**
    * @Author GroundDemo
    * @Description 批量查询
    * @Date 18:09 2025/3/24
    * @Param [keys]
    * @return java.lang.String
    **/
    public static List<String> getByKeys(List<String> keys) {
        return staticRedisTemplate.opsForValue().multiGet(keys);
    }

    /**
    * @Author GroundDemo
    * @Description 批量查询HyperLogLog的数量
    * @Date 20:11 2025/3/24
    * @Param [keys]
    * @return java.util.List<java.lang.String>
    **/
    public static List<String> getHyperLogBatch(List<String> keys) {
        List<String> res = new ArrayList<>();
        for (String key : keys) {
            Long size = staticRedisTemplate.opsForHyperLogLog().size(key);
            res.add(String.valueOf(size));
        }
        return res;
    }

    /**
    * @Author GroundDemo
    * @Description 获取所有的Hash数据
    * @Date 16:39 2025/3/22
    * @Param [key]
    * @return java.util.Map<java.lang.Object,java.lang.Object>
    **/
    public static Map<Object, Object> getAllHashInfo(String key) {
        return staticRedisTemplate.opsForHash().entries(key);
    }

    /**
    * @Author GroundDemo
    * @Description 获取hash内容
    * @Date 10:39 2025/3/22
    * @Param [key, hashKey]
    * @return java.lang.String
    **/
    public static String getHashByKey(String key, String hashKey) {
        if (Boolean.FALSE.equals(RedisUtils.hasHashKey(key, hashKey))) {
            return null;
        }
        return Objects.requireNonNull(staticRedisTemplate.opsForHash().get(key, hashKey)).toString();
    }

    /**
    * @Author GroundDemo
    * @Description 判断key是否存在
    * @Date 21:20 2024/5/30
    * @Param [key]
    * @return boolean
    **/
    public static boolean hasKey(String key) {
        return Boolean.TRUE.equals(staticRedisTemplate.hasKey(key));
    }

    /**
    * @Author GroundDemo
    * @Description 判断hash类型中是否存在key
    * @Date 10:34 2025/3/22
    * @Param [key, hashKey]
    * @return boolean
    **/
    public static boolean hasHashKey(String key, String hashKey) {
        return staticRedisTemplate.opsForHash().hasKey(key, hashKey);
    }

    /**
    * @Author GroundDemo
    * @Description 删除缓存的键值
    * @Date 15:55 2024/4/13
    * @Param [key]
    * @return void
    **/
    public static void deleteByKey(String key) {
        staticRedisTemplate.delete(key);
    }

    /**
    * @Author GroundDemo
    * @Description 批量删除数据
    * @Date 19:30 2025/3/22
    * @Param [keys]
    * @return void
    **/
    public static void deleteByKeys(Set<String> keys) {
        staticRedisTemplate.delete(keys);
    }

    /**
    * @Author GroundDemo
    * @Description keys xxx*
    * @Date 19:29 2025/3/22
    * @Param [pattern]
    * @return java.util.Set<java.lang.String>
    **/
    public static Set<String> getKeys(String pattern) {
        return staticRedisTemplate.keys(pattern);
    }

    /**
    * @Author GroundDemo
    * @Description 删除hash中的key
    * @Date 12:06 2025/3/22
    * @Param [key, hashKey]
    * @return void
    **/
    public static void deleteHashKey(String key, String hashKey) {
        staticRedisTemplate.opsForHash().delete(key, hashKey);
    }

    /**
    * @Author GroundDemo
    * @Description 获取键的类型
    * @Date 16:30 2025/3/22
    * @Param [key]
    * @return org.springframework.data.redis.connection.DataType
    **/
    public static DataType geKeyType(String key) {
        return staticRedisTemplate.type(key);
    }

    /**
    * @Author GroundDemo
    * @Description redis加锁
    * @Date 10:11 2024/9/16
    * @Param [key, expire, uid]
    * @return boolean
    **/
    public static boolean lock(String key, long expire, String uid) {
        try {
            RedisCallback<String> execCommand = redisConnection -> {
                JedisCommands commands = (JedisCommands) redisConnection.getNativeConnection();
                // redis命令执行参数设置 nx - 不存在则进行插入，存在则插入失败  px - 过期时间
                SetParams params = new SetParams();
                params.nx();
                params.px(expire);
                return commands.set(key, uid, params);
            };
            // 执行Redis命令
            Object result = staticRedisTemplate.execute(execCommand);
            if (Objects.nonNull(result)) {
                return true;
            }
        } catch (Exception e) {
            log.error("RedisUtils lock error {}", e.getMessage());
        }
        return false;
    }


    /**
    * @Author GroundDemo
    * @Description 释放锁
    * @Date 10:16 2024/9/16
    * @Param [key, uid]
    * @return boolean
    **/
    public static boolean unLock(String key, String uid) {
        try {
            // 适配lua脚本，匹配key和uid值，uid值一样才可释放key
            List<String> keys = new ArrayList<>();
            List<String> args = new ArrayList<>();
            keys.add(key);
            args.add(uid);

            RedisCallback<Long> callback = redisConnection -> {
                Object nativeConnection = redisConnection.getNativeConnection();
                Jedis jedis = (Jedis) nativeConnection;
                // 采用LUA脚本方式执行
                Object eval = jedis.eval(DELETE_LOCK_LUA.toString(), keys, args);
                return (Long) eval;
            };
            // 执行脚本
            Long result = staticRedisTemplate.execute(callback);
            // 执行结果为0失败，否则成功
            if (Objects.nonNull(result) && result > 0) {
                return true;
            }
        } catch (Exception e) {
            log.error("RedisUtils unLock error {}", e.getMessage());
        }
        return false;
    }
}
